package edu.bath.sensorframework.sensor;

import java.io.UnsupportedEncodingException;

import org.apache.commons.lang3.StringEscapeUtils;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.pubsub.AccessModel;
import org.jivesoftware.smackx.pubsub.ConfigureForm;
import org.jivesoftware.smackx.pubsub.FormType;
import org.jivesoftware.smackx.pubsub.LeafNode;
import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.PubSubManager;
import org.jivesoftware.smackx.pubsub.PublishModel;
import org.jivesoftware.smackx.pubsub.SimplePayload;

import edu.bath.sensorframework.Config;
import edu.bath.sensorframework.DataReading;

/**
 * Sensor class, should be extended by any agent wishing to act as a sensor 
 * within the framework. This uses a publish/subscribe design pattern, with 
 * other agents sending a subscription request message to the sensor, which 
 * then publishes sensor data to all subscribers as it becomes available.
 * 
 * All sensors should periodically call one of the receiveSensorMessages 
 * methods in order to pick up messages directed at the sensor framework 
 * rather than the sensor itself.
 * 
 * @author adan
 *
 */
public abstract class Sensor {
	private LeafNode leaf;
	private PubSubManager mgr;
	private String nodeName;
	private Connection connection;
	private String username, password;
	
	/**
	 * Creates a sensor.
	 * @throws XMPPException 
	 */
	public Sensor(String serverAddress, String serverName, String id, String password, String nodeName) throws XMPPException {
		// Set up XMPP server connection first
		Config.configure();
		connection = new XMPPConnection(serverAddress);
		connection.connect();
		connection.login(id, password);
		this.username = id; this.password = password;
		if(connection.isConnected())
			System.out.println("Now connected!");
		else
			System.out.println("Not connected!");
		mgr = new PubSubManager(connection, "pubsub."+serverName);
		
		this.nodeName = nodeName;
		try {
			leaf = mgr.createNode(nodeName);
			ConfigureForm form = new ConfigureForm(FormType.submit);
			form.setAccessModel(AccessModel.open);
			form.setDeliverPayloads(true);
			form.setNotifyRetract(false);
			form.setPersistentItems(false);
			form.setPublishModel(PublishModel.open);
			leaf.sendConfigurationForm(form);
		} catch(Exception e) {
			System.out.println("Node creation failed, fetching old one.");
			leaf = (LeafNode)mgr.getNode(nodeName);
		}
	}
	
	/**
	 * Publishes an item of data to all current subscribers.
	 * @param data
	 * @throws UnsupportedEncodingException 
	 * @throws RDFHandlerException 
	 * @throws RepositoryException 
	 */
	protected void publish(DataReading data) throws UnsupportedEncodingException {
		leaf.publish(new PayloadItem<SimplePayload>("pwrsensor"+ System.currentTimeMillis(), 
				new SimplePayload("RDF", "http://www.w3.org/1999/02/22-rdf-syntax-ns#", "<RDF>"+StringEscapeUtils.escapeXml(data.toRDF())+"</RDF>")));
	}
	
	protected void cleanup() throws XMPPException {
		mgr.deleteNode(nodeName);
	}
	
	protected boolean checkReconnect() throws XMPPException {
		boolean output = false;
		if(!connection.isConnected()) {
			System.out.println("Not connected!");
			connection.connect();
			output = true;
		}
		if(!connection.isAuthenticated()) {
			System.out.println("Not authenticated!");
			connection.login(username, password);
			output = true;
		}
		return output;
	}
}
